/*
 * File Name: server.c
 *
 * Group Members: Cassie Chin (cassiechin9793@gmail.com)
 *                Stephen Smetana (stephensmetana11@gmail.com)
 *                Logan Widick (logan.widick@gmail.com)
 *
 * File Description: 
 *
 *
 * Sources: http://beej.us/guide/bgnet/examples/server.c
 *          This code is originally intended to send the string "Hello, World!\n" 
 *          out over a stream connection.
 *
 *          We took the outline of this code and incorporated the ability to 
 *          perform actions in a child process. This child process recieves
 *          HTTP requests from a client.
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>

/* We can delete everything we don't need here before we turn in the project 
   Turn statement to ON to use any of these features */
#define DEBUG_OFF
#define SETSOCKOPT_OFF
#define REAP_DEAD_PROCESSES_OFF

#define PORT "3490"  // the port users will be connecting to
#define BACKLOG 10     // how many pending connections queue will hold
#define BUFFERSIZE 256

void main_child (int fd);

void sigchld_handler(int s) {
  while(waitpid(-1, NULL, WNOHANG) > 0);
}

// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa) {
  if (sa->sa_family == AF_INET) {
    return &(((struct sockaddr_in*)sa)->sin_addr);
  }

  return &(((struct sockaddr_in6*)sa)->sin6_addr);
}

int main(void) {
  int sockfd, new_fd;  // listen on sock_fd, new connection on new_fd
  struct addrinfo hints, *servinfo, *p;
  struct sockaddr_storage their_addr; // connector's address information
  socklen_t sin_size;
  struct sigaction sa;
  int yes=1;
  char s[INET6_ADDRSTRLEN];
  int rv;

  memset(&hints, 0, sizeof hints);
  hints.ai_family = AF_UNSPEC;
  hints.ai_socktype = SOCK_STREAM;
  hints.ai_flags = AI_PASSIVE; // use my IP

  if ((rv = getaddrinfo(NULL, PORT, &hints, &servinfo)) != 0) {
    fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
    return 1;
  }

  // loop through all the results and bind to the first we can
  for(p = servinfo; p != NULL; p = p->ai_next) {
    if ((sockfd = socket(p->ai_family, p->ai_socktype,
			 p->ai_protocol)) == -1) {
      perror("server: socket");
      continue;
    }

#ifdef SETSOCKOPT_ON
    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes,
		   sizeof(int)) == -1) {
      perror("setsockopt");
      exit(1);
    }
#endif

    if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
      close(sockfd);
      perror("server: bind");
      continue;
    }

    break;
  }

  if (p == NULL)  {
    fprintf(stderr, "server: failed to bind\n");
    return 2;
  }

  freeaddrinfo(servinfo); // all done with this structure

  if (listen(sockfd, BACKLOG) == -1) {
    perror("listen");
    exit(1);
  }

#ifdef REAP_DEAD_PROCESSES_ON
  sa.sa_handler = sigchld_handler; // reap all dead processes
  sigemptyset(&sa.sa_mask);
  sa.sa_flags = SA_RESTART;
  if (sigaction(SIGCHLD, &sa, NULL) == -1) {
    perror("sigaction");
    exit(1);
  }
#endif

  printf("server: waiting for connections...\n");

  while(1) {  // main accept() loop
    sin_size = sizeof their_addr;
    new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
    if (new_fd == -1) {
      perror("accept");
      continue;
    }

    inet_ntop(their_addr.ss_family, get_in_addr((struct sockaddr *)&their_addr), s, sizeof s);
    printf("server: got connection from %s\n", s);

    /* BEGIN CHILD PROCESS : HTTP request processing is done here*/
    if (!fork()) { 
      close(sockfd); // child doesn't need the listener
      main_child (new_fd);
      close(new_fd);
      exit(0);
    }
    /* END CHILD PROCESS */
    close(new_fd);  // parent doesn't need this
  }

  return 0;
}

void main_child (int new_fd) {
    int bytesReceived = 0;
    char message [BUFFERSIZE];
    //    struct sockaddr_in client_address;

    while (1) {
	bytesReceived = recv(new_fd, message, BUFFERSIZE, 0);
	if (strncmp (message, "exit", 4) == 0)
	  break;
	//bytesReceived = read (client_sockfd, message, BUFFERSIZE);
	message[bytesReceived] = '\0';

#ifdef DEBUG_ON
	printf ("bytesReceived = %d\n", bytesReceived);
	printf ("strlen = %d\n", strlen(message));
	printf ("received from %s:  %s\n", inet_ntoa (client_address.sin_addr), message);
#endif
	printf ("received message:  %s\n", message);

	// PARSE HTTP request here

	write (new_fd, message, bytesReceived-1);
    }

    close(new_fd);
    exit(0);
}
